﻿Imports System.Data.OleDb
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Imports Microsoft.ML
Imports Microsoft.Office.Interop.Access

Public Class E3_モデルデータ作成F
#Region "画面操作"

    ' クラスレベル変数はK1.vbで宣言
    Private 閉じる要求 As Boolean = False

    ' Windows API for keyboard state
    <DllImport("user32.dll")>
    Private Shared Function GetAsyncKeyState(ByVal vKey As Keys) As Short
    End Function

    Private Sub E3_モデルデータ作成F_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        著作L.Text = 著作
        フォルダー確認()
        My_DATA2読込()   'ベースE数 ベースI数 を読入る
        ' フォームの初期設定のみ
        進行状況L.Text = "..."
    End Sub
    Private Sub E3_モデルデータ作成実行Bt_Click(sender As Object, e As EventArgs) Handles E3_モデルデータ作成実行Bt.Click
        Try
            進行読込()
        Catch ex As Exception
            MsgBox($"処理エラー: {ex.Message}{vbCrLf}{ex.StackTrace}", MsgBoxStyle.Critical)
            進行状況L.Text = "エラーが発生しました。"
        End Try
    End Sub
    Private Sub E3_閉じるBt_Click(sender As Object, e As EventArgs) Handles E3_閉じるBt.Click
        'Dim result = MsgBox("フォームを閉じますか？",
        '                    MsgBoxStyle.Question Or MsgBoxStyle.YesNo, "閉じる確認")
        'If result = DialogResult.Yes Then
        閉じる要求 = True
        E2_モデルデータ設定F.Close()
        Me.Close()
        'End If
    End Sub
    Private Sub データモデルDgv非表示Rb_CheckedChanged(sender As Object, e As EventArgs) Handles データモデルDgv非表示Rb.CheckedChanged, データモデルDgv表示Rb.CheckedChanged
        ' ラジオボタンの状態に応じてデータモデルDgvの表示/非表示を切り替え
        Try
            If データモデルDgv表示Rb.Checked = True Then
                T600_データモデル表示()
            Else
                ' 非表示
                データモデルDgv.DataSource = Nothing
                データモデルDgv.Visible = False
            End If
        Catch ex As Exception
            ' エラーが発生しても処理を継続（スルー）
        End Try
    End Sub
#End Region

#Region "E3_モデルデータ作成実行Btの処理"

#End Region

    Private Sub 進行読込()
        'MySQL = "UPDATE T160_全出荷E集計 SET T160_全出荷E集計.判定 = ""Yes"" WHERE (((T160_全出荷E集計.乱数)<=0.7));"

        'T300_進行の読込、作成Chk=済が有ればスキップ
        Dim dbPath As String = Tera計算DataPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"
        Dim connStr As String = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath};"

        Try
            ' まずT300_進行テーブルのデータを全て取得
            Dim 進行データリスト As New List(Of (進行連番 As Integer, 計算E数 As Integer, 計算I数 As Integer, E採用判定値 As Double, I採用判定値 As Double, 作成Chk As String))

            Using conn As New OleDbConnection(connStr)
                conn.Open()

                ' T300_進行テーブルから作成Chk <> "済"のデータを進行連番順に読み込む
                Dim sql As String = "SELECT * FROM T300_進行 WHERE 作成Chk <> '済' OR 作成Chk IS NULL ORDER BY 進行連番"
                Using cmd As New OleDbCommand(sql, conn)
                    Using reader As OleDbDataReader = cmd.ExecuteReader()
                        While reader.Read()
                            Dim データ = (
                                進行連番:=Convert.ToInt32(reader("進行連番")),
                                計算E数:=Convert.ToInt32(reader("計算E数")),
                                計算I数:=Convert.ToInt32(reader("計算I数")),
                                E採用判定値:=Convert.ToDouble(reader("E採用判定値")),
                                I採用判定値:=Convert.ToDouble(reader("I採用判定値")),
                                作成Chk:=If(IsDBNull(reader("作成Chk")), "", reader("作成Chk").ToString())
                            )
                            進行データリスト.Add(データ)
                        End While
                    End Using
                End Using
            End Using

            ' データが取得できたか確認
            If 進行データリスト.Count = 0 Then
                進行状況L.Text = "処理対象のレコードがありません。"
                進行状況L.Refresh()
                MsgBox("処理対象のレコードがありません。" & vbCrLf &
                      "全てのレコードが既に処理済みです。", MsgBoxStyle.Information, "処理対象なし")
                Exit Sub
            End If

            ' 各レコードを処理（SQLでフィルタ済みのため全て処理対象）
            Dim 処理件数 As Integer = 0
            Dim 全件数 As Integer = 進行データリスト.Count
            Dim 中断フラグ As Boolean = False

            For Each データ In 進行データリスト
                ' 閉じるボタンによる中断チェック
                If 閉じる要求 Then
                    中断フラグ = True
                    進行状況L.Text = $"閉じるボタンにより中断されました。({処理件数}/{全件数} 件完了)"
                    進行状況L.Refresh()
                    Exit For
                End If

                進行連番 = データ.進行連番
                計算E数 = データ.計算E数
                計算I数 = データ.計算I数
                E採用判定値 = データ.E採用判定値
                I採用判定値 = データ.I採用判定値

                Try
                    ' 処理開始メッセージ
                    進行状況L.Text = $"進行連番 {進行連番} の処理を開始します。 - Escキーで中断可能"
                    進行状況L.Refresh()
                    System.Windows.Forms.Application.DoEvents()

                    ' Escapeキーのチェック
                    If My.Computer.Keyboard.CtrlKeyDown = False AndAlso My.Computer.Keyboard.AltKeyDown = False AndAlso My.Computer.Keyboard.ShiftKeyDown = False Then
                        If GetAsyncKeyState(Keys.Escape) <> 0 Then
                            Dim result As DialogResult = MsgBox($"処理を中断しますか？{vbCrLf}{vbCrLf}現在の進行状況: {処理件数}/{全件数} 件完了{vbCrLf}{vbCrLf}「はい」: 処理を中断して終了します。{vbCrLf}「いいえ」: 処理を続行します。", MsgBoxStyle.Question Or MsgBoxStyle.YesNo, "処理の中断確認")
                            If result = DialogResult.Yes Then
                                中断フラグ = True
                                進行状況L.Text = $"処理が中断されました。({処理件数}/{全件数} 件完了)"
                                進行状況L.Refresh()
                                MsgBox($"処理を中断しました。{vbCrLf}{vbCrLf}完了件数: {処理件数}/{全件数}", MsgBoxStyle.Information, "処理中断")
                                Exit For
                            End If
                        End If
                    End If
                    Clipboard.SetText(CStr(ベースE数)) ' 変数の値をクリップボードにコピー

                    発送先採用判定("T160_全出荷E集計", 計算条件, 計算E数, E採用判定値)
                    アイテム採用判定("T160_全出荷I集計", 計算条件, 計算I数, I採用判定値)
                    T350_作成(ベースE数, 計算E数, ベースI数, 計算I数)
                    T500_計算対象作成()
                    T500_計算対象集計と変数読込(進行連番)
                    T600_データモデル更新0_89(進行連番)
                    T600_データモデル更新90_164(進行連番)

                    'T600_データモデル作成()   ' T500_計算対象からデータモデル作成


                    If 進行連番 = 1 Then
                        Close()
                    End If

                    進行状況L.Text = $"進行連番 {進行連番}: T500_計算対象を作成中... ({処理件数 + 1}/{全件数})"
                    進行状況L.Refresh()
                    System.Windows.Forms.Application.DoEvents()




                    ' 処理完了後、作成Chkを"済"に更新
                    Using conn As New OleDbConnection(connStr)
                        conn.Open()
                        Dim sqlUpdate As String = "UPDATE T300_進行 Set 作成Chk = ? WHERE 進行連番 = ?"
                        Using cmdUpdate As New OleDbCommand(sqlUpdate, conn)
                            cmdUpdate.Parameters.Add("@作成Chk", OleDbType.VarChar, 10).Value = "済"
                            cmdUpdate.Parameters.AddWithValue("@進行連番", 進行連番)
                            cmdUpdate.ExecuteNonQuery()
                        End Using
                    End Using

                    処理件数 += 1
                    進行状況L.Text = $"進行連番 {進行連番} の処理が完了しました。({処理件数}/{全件数})"
                    進行状況L.Refresh()
                    System.Windows.Forms.Application.DoEvents()

                    ' Accessが肥大化するため、一定件数ごとに最適化(Compact)を実行
                    Try
                        If 処理件数 > 0 AndAlso (処理件数 Mod 50) = 0 Then
                            進行状況L.Text = $"進行連番 {進行連番}: データベース最適化(Compact)を開始します... ({処理件数}/{全件数})"
                            進行状況L.Refresh()
                            System.Windows.Forms.Application.DoEvents()

                            ' GCで接続解放を促してからCompactを実行
                            GC.Collect()
                            GC.WaitForPendingFinalizers()

                            'CompactAccess(dbPath)

                            進行状況L.Text = $"進行連番 {進行連番}: データベース最適化(Compact)が完了しました。 ({処理件数}/{全件数})"
                            進行状況L.Refresh()
                            System.Windows.Forms.Application.DoEvents()
                        End If
                    Catch ex As Exception
                        ' 最適化に失敗しても処理は継続（ログ表示はせずスルー）
                    End Try

                Catch ex As Exception
                    MsgBox($"進行連番 {進行連番} の処理でエラー: {ex.Message}{vbCrLf}{ex.StackTrace}", MsgBoxStyle.Critical)
                    Exit Sub
                End Try
            Next

            If 中断フラグ Then
                ' 中断された場合は何もしない（既にメッセージ表示済み）
            ElseIf 処理件数 > 0 Then
                進行状況L.Text = $"モデルデータ作成が完了しました。処理件数: {処理件数}/{全件数}"
                進行状況L.Refresh()
                MsgBox($"モデルデータ作成が完了しました。{vbCrLf}処理件数: {処理件数}/{全件数}", MsgBoxStyle.Information)
                If 処理件数 = 全件数 Then
                    データモデル_csv作成()
                End If

            Else
                進行状況L.Text = "処理対象のレコードがありませんでした。"
                進行状況L.Refresh()
            End If

            MsgBox($"モデルデータ作成が完了しました。{vbCrLf}処理件数: {処理件数}/{全件数}", MsgBoxStyle.Information)


        Catch ex As Exception
            MsgBox($"進行読込エラー: {ex.Message}{vbCrLf}{ex.StackTrace}", MsgBoxStyle.Critical)
        End Try
    End Sub


    Private Sub データモデル_csv作成()
        ' T600_データモデルを「EIQ分析AI化」フォルダーにデータモデル.csvを新規保存
        Dim dbPath As String = Tera計算DataPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"
        Dim connStr As String = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath};"

        Try
            Dim outDir As String = Tera計算DataPath & "\EIQ分析AI化"
            If Not Directory.Exists(outDir) Then
                Directory.CreateDirectory(outDir)
            End If

            Dim outPath As String = Path.Combine(outDir, "データモデル.csv")

            Using conn As New OleDbConnection(connStr)
                conn.Open()

                ' テーブル存在チェック
                Dim テーブル存在 As Boolean = False
                Dim schema As DataTable = conn.GetSchema("Tables")
                For Each row As DataRow In schema.Rows
                    If row("TABLE_NAME").ToString().ToUpper() = "T600_データモデル".ToUpper() Then
                        テーブル存在 = True
                        Exit For
                    End If
                Next

                If Not テーブル存在 Then
                    MsgBox("テーブル T600_データモデル が存在しません。", MsgBoxStyle.Exclamation)
                    Exit Sub
                End If

                Dim sql As String = "SELECT * FROM T600_データモデル ORDER BY モデル連番"
                Using cmd As New OleDbCommand(sql, conn)
                    Using reader As OleDbDataReader = cmd.ExecuteReader()
                        If reader Is Nothing Then
                            MsgBox("T600_データモデル のデータ取得に失敗しました。", MsgBoxStyle.Exclamation)
                            Exit Sub
                        End If

                        Using sw As New StreamWriter(outPath, False, System.Text.Encoding.UTF8)
                            Dim colCount As Integer = reader.FieldCount

                            ' --- ヘッダ行をカンマで結合 ---
                            Dim headers As New List(Of String)
                            For i As Integer = 0 To colCount - 1
                                headers.Add(reader.GetName(i))
                            Next
                            sw.WriteLine(String.Join(",", headers))

                            ' --- データ行をカンマで結合 ---
                            While reader.Read()
                                Dim fields As New List(Of String)
                                For i As Integer = 0 To colCount - 1
                                    Dim val As String = If(IsDBNull(reader(i)), "", reader(i).ToString())

                                    ' カンマ区切りを維持するため、データ内のカンマ・タブ・改行を半角スペースに置換
                                    val = val.Replace(",", " ").Replace(vbTab, " ").Replace(vbCr, " ").Replace(vbLf, " ")
                                    fields.Add(val)
                                Next
                                sw.WriteLine(String.Join(",", fields))
                            End While
                        End Using
                    End Using
                End Using
            End Using

            MsgBox($"データモデル.csvを保存しました。{vbCrLf}{outPath}", MsgBoxStyle.Information)

        Catch ex As Exception
            MsgBox($"データモデル_csv作成エラー: {ex.Message}{vbCrLf}{ex.StackTrace}", MsgBoxStyle.Critical)
        End Try
    End Sub

#Region " T500_計算対象作成"
    Private Sub T500_計算対象作成()
        ' T500_計算対象作成

        Dim dbPath As String = MyDB
        Dim connStr As String = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath};"
        ' 1. テーブル削除（エラーを無視する設定で呼び出し）
        DEL_table("T50*")
        ' 2. T500_計算対象 テーブル作成
        MySQL = "SELECT T200.* INTO T500_計算対象 FROM T350 INNER JOIN T200 ON T350.行連番 = T200.行連番 WHERE (((T350.E判定)='Yes')) OR (((T350.I判定)='Yes'));"
        SQL実行(MySQL)

        '３．T200テーブルから計算対象をコピーする
        MySQL = "UPDATE T350 INNER JOIN T500_計算対象 ON T350.行連番 = T500_計算対象.行連番 SET T500_計算対象.出荷先 = [T350]![出荷先], T500_計算対象.[アイテム] = [T350]![アイテム];"
        SQL実行(MySQL)

        If ベースE数 < 計算E数 Or ベースI数 < 計算E数 Then
            '４. T500_計算対象にT200テーブルを全コピーする（採用数がベース数を上回る場合）
            MySQL = "INSERT INTO T500_計算対象 SELECT T200.* FROM T200;"
            SQL実行(MySQL)
        End If

    End Sub

    '''' <summary>
    ''' T590_*一時テーブルをOleDb接続で削除（ADOX不使用）
    ''' </summary>
    Private Sub T590一時テーブル削除(conn As OleDbConnection)
        Try
            Dim schema As DataTable = conn.GetSchema("Tables")
            For Each row As DataRow In schema.Rows
                Dim tableName As String = row("TABLE_NAME").ToString()
                If tableName.StartsWith("T590_", StringComparison.OrdinalIgnoreCase) Then
                    Try
                        Using cmdDrop As New OleDbCommand($"DROP TABLE [{tableName}]", conn)
                            cmdDrop.ExecuteNonQuery()
                        End Using
                    Catch
                        ' テーブルが削除できない場合は無視
                    End Try
                End If
            Next
        Catch
            ' スキーマ取得失敗時は無視
        End Try
    End Sub

#End Region

#Region " T600_データモデル作成"

    Private Sub T600_データモデル表示()
        Dim dbPath As String = Tera計算DataPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"
        Dim connStr As String = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath};"

        Try
            Using conn As New OleDbConnection(connStr)
                conn.Open()

                ' T600_データモデルテーブルが存在するか確認
                Dim テーブル存在 As Boolean = False
                Dim dt As DataTable = conn.GetSchema("Tables")
                For Each row As DataRow In dt.Rows
                    If row("TABLE_NAME").ToString().ToUpper() = "T600_データモデル".ToUpper() Then
                        テーブル存在 = True
                        Exit For
                    End If
                Next

                If Not テーブル存在 Then
                    ' テーブルが存在しない場合はスルー
                    データモデルDgv.Visible = False
                    Exit Sub
                End If

                ' データが存在するか確認
                Dim sqlCount As String = "SELECT COUNT(*) FROM T600_データモデル"
                Using cmdCount As New OleDbCommand(sqlCount, conn)
                    Dim count As Integer = Convert.ToInt32(cmdCount.ExecuteScalar())
                    If count = 0 Then
                        ' データが無い場合はスルー
                        データモデルDgv.Visible = False
                        Exit Sub
                    End If
                End Using

                ' データを取得して表示
                Dim sql As String = "SELECT * FROM T600_データモデル ORDER BY モデル連番"
                Dim adapter As New OleDbDataAdapter(sql, conn)
                Dim dtData As New DataTable()
                adapter.Fill(dtData)

                データモデルDgv.DataSource = dtData
                データモデルDgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
                データモデルDgv.Visible = True

                ' データバインド完了後に画面を更新
                データモデルDgv.Refresh()
                System.Windows.Forms.Application.DoEvents()

                ' 更新した進行連番の行を表示
                If データモデルDgv.Rows.Count > 0 AndAlso 進行連番 > 0 Then
                    ' モデル連番列のインデックスを取得
                    Dim モデル連番列Index As Integer = -1
                    For i As Integer = 0 To データモデルDgv.Columns.Count - 1
                        If データモデルDgv.Columns(i).Name.ToUpper() = "モデル連番" Then
                            モデル連番列Index = i
                            Exit For
                        End If
                    Next

                    ' モデル連番に一致する行を検索
                    If モデル連番列Index >= 0 Then
                        For i As Integer = 0 To データモデルDgv.Rows.Count - 1
                            If データモデルDgv.Rows(i).IsNewRow Then Continue For

                            Dim セル値 = データモデルDgv.Rows(i).Cells(モデル連番列Index).Value
                            If セル値 IsNot Nothing AndAlso Not IsDBNull(セル値) Then
                                Dim モデル連番値 As Integer = Convert.ToInt32(セル値)
                                If モデル連番値 = 進行連番 Then
                                    ' 該当行が見つかった
                                    データモデルDgv.ClearSelection()
                                    データモデルDgv.Rows(i).Selected = True

                                    ' 該当行の5行上を表示（スクロール）
                                    Dim スクロール位置 As Integer = Math.Max(0, i - 5)
                                    データモデルDgv.FirstDisplayedScrollingRowIndex = スクロール位置

                                    ' 画面を更新
                                    データモデルDgv.Refresh()
                                    System.Windows.Forms.Application.DoEvents()
                                    Exit For
                                End If
                            End If
                        Next
                    End If
                End If

            End Using
        Catch ex As Exception
            ' エラーが発生してもスルー（データが無い場合も含む）
            データモデルDgv.Visible = False
        End Try
    End Sub
#End Region

    Private Sub T500_計算対象集計と変数読込(進行連番 As Integer)
        ' 1. 配列の初期化
        Array.Clear(N, 0, N.Length)

        ' 接続文字列
        Dim connStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";"

        Using conn As New OleDbConnection(connStr)

            Dim modelNo As Integer = 進行連番

            Try
                conn.Open()

                ' --- クエリの作成・更新セクション ---
                ' Q550_E数1 の作成/更新
                SaveQuery(conn, "Q550_E数1",
                    "SELECT GPLEランク, 出荷先 FROM T500_計算対象 GROUP BY GPLEランク, 出荷先;")

                ' Q550_E数2 の作成/更新
                ' modelNo の値を SQL に埋め込んでリテラル列として出力する（Access の VIEW では VB 変数は使えないため）
                Dim sqlE2 As String = "SELECT " & modelNo.ToString() & " AS モデル連番, GPLEランク, Count(出荷先) AS 出荷先数 FROM Q550_E数1 GROUP BY GPLEランク;"
                SaveQuery(conn, "Q550_E数2", sqlE2)

                ' Q550_I数1 の作成/更新
                SaveQuery(conn, "Q550_I数1",
                    "SELECT GPLIランク, [アイテム] FROM T500_計算対象 GROUP BY GPLIランク, [アイテム];")

                ' Q550_I数2 の作成/更新
                SaveQuery(conn, "Q550_I数2",
                    "SELECT GPLIランク, Count(アイテム) AS アイテム数 FROM Q550_I数1 GROUP BY GPLIランク;")

                ' Q560_ランク分け の作成/更新
                SaveQuery(conn, "Q560_ランク分け",
                    "SELECT GPLEランク, GPLIランク, Count(G行) AS 行数, Sum(Gバ) AS バラ数, Sum(Gケ) AS ケース数, " &
                    "Sum(GPL) AS PL数, Sum(G容) AS 容積, Sum(G重) AS 重量 " &
                    "FROM T500_計算対象 GROUP BY GPLEランク, GPLIランク;")

                ' --- デバッグ: クエリ結果をCSVにダンプ（問題の切り分け用） ---
                Try
                    Dim debugDir As String = Tera計算DataPath & "\EIQ分析AI化\debug"
                    If Not Directory.Exists(debugDir) Then Directory.CreateDirectory(debugDir)
                    DumpQueryToCsv(conn, "SELECT * FROM Q550_E数2", Path.Combine(debugDir, "Q550_E数2.csv"))
                    DumpQueryToCsv(conn, "SELECT * FROM Q550_I数2", Path.Combine(debugDir, "Q550_I数2.csv"))
                    DumpQueryToCsv(conn, "SELECT * FROM Q560_ランク分け", Path.Combine(debugDir, "Q560_ランク分け.csv"))
                Catch exDump As Exception
                    ' デバッグ失敗は処理継続
                End Try

                ' --- データの読み込みセクション ---

                ' 2. Eランク数読み込み (N5-N9)
                FillArray(conn, "SELECT * FROM Q550_E数2", "GPLEランク", "出荷先数", 5)

                ' 3. Iランク数読み込み (N10-N14)
                FillArray(conn, "SELECT * FROM Q550_I数2", "GPLIランク", "アイテム数", 10)

                ' 4. ランク分けマトリックス読み込み (N15-N164)
                Using cmd As New OleDbCommand("SELECT * FROM Q560_ランク分け", conn)
                    Using reader As OleDbDataReader = cmd.ExecuteReader()
                        While reader.Read()
                            Dim eIdx As Integer = GetRankIndex(reader("GPLEランク").ToString())
                            Dim iIdx As Integer = GetRankIndex(reader("GPLIランク").ToString())

                            If eIdx >= 0 And iIdx >= 0 Then
                                Dim pos As Integer = (eIdx * 5) + iIdx
                                N(15 + pos) = Nz(reader("行数"))
                                N(40 + pos) = Nz(reader("バラ数"))
                                N(65 + pos) = Nz(reader("ケース数"))
                                N(90 + pos) = Nz(reader("PL数"))
                                N(115 + pos) = Nz(reader("容積"))
                                N(140 + pos) = Nz(reader("重量"))
                            End If
                        End While
                    End Using
                End Using

                N(0) = 進行連番
                N(1) = N(5) + N(6) + N(7) + N(8) + N(9)      '発送先数
                N(2) = N(10) + N(11) + N(12) + N(13) + N(14) 'アイテム数
                For i = 0 To 24
                    N(3) += N(15 + i)                        '行数
                    N(4) += N(40 + i)                        'バラ数
                Next

                ' MsgBox("クエリ保存および変数読込が正常に完了しました。")

            Catch ex As Exception
                MsgBox("エラー: " & ex.Message)
            End Try
        End Using
    End Sub


    ''' <summary>
    ''' Access内にクエリを保存(CREATE VIEW)する。既に存在する場合は一度削除して再作成する。
    ''' </summary>
    Private Sub SaveQuery(conn As OleDbConnection, queryName As String, sql As String)
        ' 既存のクエリを削除（存在しない場合のエラーは無視する）
        Try
            Using cmd As New OleDbCommand($"DROP VIEW {queryName}", conn)
                cmd.ExecuteNonQuery()
            End Using
        Catch
            ' クエリが存在しない場合はここに来るが無視してOK
        End Try

        ' クエリの新規作成
        Using cmd As New OleDbCommand($"CREATE VIEW {queryName} AS {sql}", conn)
            cmd.ExecuteNonQuery()
        End Using
    End Sub

    ''' <summary>
    ''' 単一ランクの集計結果を配列に流し込む
    ''' </summary>
    Private Sub FillArray(conn As OleDbConnection, sql As String, rankCol As String, valCol As String, startIdx As Integer)
        Using cmd As New OleDbCommand(sql, conn)
            Using reader As OleDbDataReader = cmd.ExecuteReader()
                While reader.Read()
                    Dim rIdx As Integer = GetRankIndex(reader(rankCol).ToString())
                    If rIdx >= 0 Then N(startIdx + rIdx) = Nz(reader(valCol))
                End While
            End Using
        End Using
    End Sub

    ''' <summary>
    ''' ランク文字(A-E)を 0-4 に変換
    ''' </summary>
    Private Function GetRankIndex(rank As String) As Integer
        If rank Is Nothing Then Return -1
        Dim s As String = rank.Trim().ToUpper()
        If s = String.Empty Then Return -1

        ' Handle single-letter ranks A-E
        Select Case s
            Case "A" : Return 0
            Case "B" : Return 1
            Case "C" : Return 2
            Case "D" : Return 3
            Case "E" : Return 4
        End Select

        ' Handle formats that contain a numeric rank suffix like E1..E5 or I1..I5
        For Each ch As Char In s
            If ch >= "1"c AndAlso ch <= "5"c Then
                Return Convert.ToInt32(ch.ToString()) - 1
            End If
        Next

        ' Not recognized
        Return -1
    End Function

    ''' <summary>
    ''' NULL値を0.0に変換するヘルパー
    ''' </summary>
    Private Function Nz(val As Object) As Double
        If IsDBNull(val) Then Return 0.0
        Return Convert.ToDouble(val)
    End Function
    Private Sub UpdateSplit(conn As OleDbConnection, id As Double, cols As List(Of String), startIdx As Integer, endIdx As Integer)
        Dim setParts As New List(Of String)
        For i As Integer = startIdx To endIdx
            setParts.Add($"[{cols(i)}] = ?")
        Next

        Dim sql As String = $"UPDATE T600_データモデル SET {String.Join(", ", setParts)} WHERE [モデル連番] = ?"

        Using cmd As New OleDbCommand(sql, conn)
            ' 値のセット
            For i As Integer = startIdx To endIdx
                cmd.Parameters.AddWithValue($"@p{i}", N(i))
            Next
            ' WHERE句のID
            cmd.Parameters.AddWithValue("@id", id)

            Dim affected As Integer = cmd.ExecuteNonQuery()

            ' 更新されなかった場合はレコードが存在しないため、INSERT を行う
            If affected = 0 Then
                ' INSERT 用のカラム一覧とプレースホルダを構築
                Dim insertCols As New List(Of String)
                Dim insertPlaceholders As New List(Of String)
                For j As Integer = 0 To cols.Count - 1
                    insertCols.Add($"[{cols(j)}]")
                    insertPlaceholders.Add("?")
                Next

                Dim insertSql As String = $"INSERT INTO T600_データモデル ({String.Join(", ", insertCols)}) VALUES ({String.Join(", ", insertPlaceholders)})"

                Using cmdIns As New OleDbCommand(insertSql, conn)
                    ' INSERT はテーブル全列分の値を渡す（N配列に無い場合は0またはDBNullを入れる）
                    For j As Integer = 0 To cols.Count - 1
                        Dim val As Object = Nothing
                        If j < N.Length Then
                            ' N の値をそのまま使用
                            val = N(j)
                        Else
                            ' 配列外は 0 とする
                            val = 0
                        End If
                        cmdIns.Parameters.AddWithValue($"@p{j}", val)
                    Next

                    cmdIns.ExecuteNonQuery()
                End Using
            End If
        End Using
    End Sub
    Private Sub T600_データモデル更新1(modelID As Double)
        ' 接続文字列
        Dim connStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";"

        Using conn As New OleDbConnection(connStr)
            Try
                conn.Open()

                ' 1. カラム名のリストをデザイン順（列番順）に取得
                Dim columnNames As New List(Of String)
                Using cmdSchema As New OleDbCommand("SELECT TOP 1 * FROM T600_データモデル", conn)
                    Using reader As OleDbDataReader = cmdSchema.ExecuteReader(CommandBehavior.SchemaOnly)
                        Dim schemaTable = reader.GetSchemaTable()
                        For Each row As DataRow In schemaTable.Rows
                            columnNames.Add(row("ColumnName").ToString())
                        Next
                    End Using
                End Using

                ' 2. UPDATE文の組み立て
                ' SET [カラム名] = ? を列番順（0〜164）に作成
                ' ※ 配列 N(0) から順に対応させるため、そのままループします
                Dim setClauses As New List(Of String)
                Dim updateCount As Integer = Math.Min(165, columnNames.Count)

                For i As Integer = 0 To updateCount - 1
                    ' カラム名にスペースや特殊文字がある場合を考慮し [] で囲む
                    setClauses.Add($"[{columnNames(i)}] = ?")
                Next

                ' SQLの構築（モデル連番をキーにして更新）
                Dim sql As String = $"UPDATE T600_データモデル SET {String.Join(", ", setClauses)} WHERE [モデル連番] = ?"

                Using cmd As New OleDbCommand(sql, conn)
                    ' 3. パラメータのセット（順番が重要！）

                    ' まず SET 句の ? に配列 N(0)～N(164) をセット
                    For i As Integer = 0 To updateCount - 1
                        cmd.Parameters.AddWithValue($"@p{i}", N(i))
                    Next

                    ' 最後に WHERE 句の [モデル連番] 用の ? をセット
                    cmd.Parameters.AddWithValue("@modelID", modelID)

                    ' 実行
                    Dim affectedRows As Integer = cmd.ExecuteNonQuery()

                    If affectedRows > 0 Then
                        ' Console.WriteLine($"モデル連番: {modelID} のレコードを更新しました。")
                    Else
                        ' レコードが存在しない場合は新規作成するか、エラーメッセージを出す
                        MsgBox($"モデル連番: {modelID} が見つかりませんでした。")
                    End If
                End Using

            Catch ex As Exception
                MsgBox("DB更新エラー: " & ex.Message)
            End Try
        End Using
    End Sub

    Private Sub T600_データモデル更新0_89(進行連番 As Integer)
        ' T600_データモデル の先頭列(設計順0～89)に配列 N(0)～N(89) の値を書き込む
        Try
            Dim connStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";"
            Using conn As New OleDbConnection(connStr)
                conn.Open()

                ' テーブルの列名を設計順（列番順）で取得
                Dim columnNames As New List(Of String)
                Using cmdSchema As New OleDbCommand("SELECT TOP 1 * FROM T600_データモデル", conn)
                    Using reader As OleDbDataReader = cmdSchema.ExecuteReader(CommandBehavior.SchemaOnly)
                        Dim schemaTable = reader.GetSchemaTable()
                        For Each row As DataRow In schemaTable.Rows
                            columnNames.Add(row("ColumnName").ToString())
                        Next
                    End Using
                End Using

                If columnNames.Count = 0 Then
                    進行状況L.Text = "T600_データモデル の列が取得できませんでした。"
                    Return
                End If

                ' 書き込む最終列インデックス（0～89 の範囲）を決定
                Dim lastIdx As Integer = Math.Min(89, columnNames.Count - 1)

                ' 実際の更新を分割関数で実行
                UpdateSplit(conn, 進行連番, columnNames, 0, lastIdx)

                進行状況L.Text = $"進行連番 {進行連番}: [0/7] データモデル列0～{lastIdx} を更新しました。"
                進行状況L.Refresh()
                System.Windows.Forms.Application.DoEvents()
            End Using
        Catch ex As Exception
            MsgBox($"T600_データモデル更新0_89 エラー: {ex.Message}{vbCrLf}{ex.StackTrace}", MsgBoxStyle.Critical)
            Throw
        End Try
    End Sub
    Private Sub T600_データモデル更新90_164(進行連番 As Integer)
        ' T600_データモデル の列90～164に配列 N(90)～N(164) の値を書き込む
        Try
            Dim connStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";"
            Using conn As New OleDbConnection(connStr)
                conn.Open()
                ' テーブルの列名を設計順（列番順）で取得
                Dim columnNames As New List(Of String)
                Using cmdSchema As New OleDbCommand("SELECT TOP 1 * FROM T600_データモデル", conn)
                    Using reader As OleDbDataReader = cmdSchema.ExecuteReader(CommandBehavior.SchemaOnly)
                        Dim schemaTable = reader.GetSchemaTable()
                        For Each row As DataRow In schemaTable.Rows
                            columnNames.Add(row("ColumnName").ToString())
                        Next
                    End Using
                End Using
                If columnNames.Count = 0 Then
                    進行状況L.Text = "T600_データモデル の列が取得できませんでした。"
                    Return
                End If
                ' 書き込む開始列インデックスと終了列インデックスを決定
                Dim startIdx As Integer = 90
                Dim endIdx As Integer = Math.Min(164, columnNames.Count - 1)
                ' 実際の更新を分割関数で実行
                UpdateSplit(conn, 進行連番, columnNames, startIdx, endIdx)
                進行状況L.Text = $"進行連番 {進行連番}: [7/7] データモデル列{startIdx}～{endIdx} を更新しました。"
                進行状況L.Refresh()
                System.Windows.Forms.Application.DoEvents()
            End Using
        Catch ex As Exception
            MsgBox($"T600_データモデル更新90_164 エラー: {ex.Message}{vbCrLf}{ex.StackTrace}", MsgBoxStyle.Critical)
            Throw
        End Try
    End Sub

    Private Sub DumpQueryToCsv(conn As OleDbConnection, sql As String, outPath As String)
        Using cmd As New OleDbCommand(sql, conn)
            Using reader As OleDbDataReader = cmd.ExecuteReader()
                Using sw As New StreamWriter(outPath, False, System.Text.Encoding.UTF8)
                    Dim colCount As Integer = reader.FieldCount
                    Dim headers As New List(Of String)
                    For i As Integer = 0 To colCount - 1
                        headers.Add(reader.GetName(i))
                    Next
                    sw.WriteLine(String.Join(",", headers))
                    While reader.Read()
                        Dim fields As New List(Of String)
                        For i As Integer = 0 To colCount - 1
                            Dim val As String = If(IsDBNull(reader(i)), "", reader(i).ToString())
                            val = val.Replace(",", " ").Replace(vbCr, " ").Replace(vbLf, " ")
                            fields.Add(val)
                        Next
                        sw.WriteLine(String.Join(",", fields))
                    End While
                End Using
            End Using
        End Using
    End Sub
End Class

